// 引入日志模块
const Logger = require('./lib/Logger');
// 引入监听器
const EventEmitter = require('events').EventEmitter;
// 引入mediasoup客户端
const mediasoupClient = require('mediasoup-client');
// 引入websocket客户端
const protooClient = require('protoo-client');
// 引入配置文件
const config = require('./config');
// 实例化日志
const logger = new Logger('roomClient');

// 视频的质量约束，分辨率
const VIDEO_CONSTRAINS = {
  qvga: { width: { ideal: 320 }, height: { ideal: 240 } },
  vga: { width: { ideal: 640 }, height: { ideal: 480 } },
  hd: { width: { ideal: 1280 }, height: { ideal: 720 } }
};

// pc 端专有约束
const PC_PROPRIETARY_CONSTRAINTS = {
  optional: [{ googDscp: true }]
};

// Used for simulcast webcam video.
const WEBCAM_SIMULCAST_ENCODINGS = [
  { scaleResolutionDownBy: 4, maxBitrate: 500000 },
  { scaleResolutionDownBy: 2, maxBitrate: 1000000 },
  { scaleResolutionDownBy: 1, maxBitrate: 5000000 }
];

// Used for VP9 webcam video.
const WEBCAM_KSVC_ENCODINGS = [
  { scalabilityMode: 'S3T3_KEY' }
];

// Used for simulcast screen sharing.
const SCREEN_SHARING_SIMULCAST_ENCODINGS = [
  { dtx: true, maxBitrate: 1500000 },
  { dtx: true, maxBitrate: 6000000 }
];

// Used for VP9 screen sharing.
const SCREEN_SHARING_SVC_ENCODINGS = [
  { scalabilityMode: 'S3T3', dtx: true }
];



// 定义存储store
let store;
// 创建客户端类
class RoomClient extends EventEmitter {

  // 定义存储缓存
  static init (data) {
    store = data.store;
  }
  /**
 * 一。构造函数
 * @param {String} roomId - 房间号
 * @param {String} peerId - 用户id
 * @param {MediaDeviceInfo} device - 客户端信息
 * @param {String} displayName - 用户名
 * @param {Boolean} useSimulcast - 是否使用同步广播，发送同一媒体源的多个不同编码流（使用不同视频编码器类型编码的相同视频源或图像分辨率）
 * @param {Boolean} useSharingSimulcast - 桌面共享中是否使用同步广播。是否发送同一媒体源的多个不同编码流（使用不同视频编码器类型编码的相同视频源或图像分辨率）
 * @param {Boolean} forceTcp - 是否希望强制使用RTP。
 * @param {Boolean} produce - 是否开启音频/视频。
 * @param {Boolean} consume - 是否接收音频/视频。
 * @param {Boolean} forceH264 - 是否强制使用H264编解码器进行发送。
 * @param {Boolean} forceVP9 - 是否强制使用VP9编解码器进行发送。
 */
  constructor({ roomId, peerId, device, displayName, useSimulcast, useSharingSimulcast, forceTcp, forceH264, forceVP9, produce, consume }) {
    logger.info('==================================mediasoup版本号::V%s', mediasoupClient.version);
    // 初始化成员属性
    // 房间是否关闭
    this._closed = false;
    // 房间唯一标识id
    this._roomId = roomId;
    // 客户端抽象层peerId，唯一对应客户端
    this._peerId = peerId;
    // 设备信息
    this._device = device;
    // 客户端展示名称
    this._displayName = displayName;
    // 摄像头是否使用同播
    this._useSimulcast = useSimulcast;
    // 桌面分享是否使用同播
    this._useSharingSimulcast = useSharingSimulcast;
    // 是否强制使用tcp
    this._forceTcp = forceTcp;
    // 视频是否强制使用h264
    this._forceH264 = forceH264;
    // 视频是否强制使用vp9
    this._forceVP9 = forceVP9;
    // 是否开启音频/视频
    this._produce = produce;
    // 是否接收音频/视频
    this._consume = consume;



    // 组装_protooUrl
    this._protooUrl = `wss://${config.https.host}:${config.https.port}/?roomId=${roomId}&peerId=${peerId}`;

    this._protoo = null;// @type {protooClient.Peer}：protoo-client Peer instance.
    this._mediasoupDevice = null;// @type {mediasoupClient.Device}：mediasoup-client Device instance.
    this._sendTransport = null;// @type {mediasoupClient.Transport}：mediasoup Transport for sending.
    this._recvTransport = null;// @type {mediasoupClient.Transport}：mediasoup Transport for receiving.
    this._micProducer = null;// @type {mediasoupClient.Producer}：Local mic mediasoup Producer.麦克风
    this._webcamProducer = null;// @type {mediasoupClient.Producer}：Local webcam mediasoup Producer.网络摄像头
    this._shareProducer = null;// @type {mediasoupClient.Producer}：Local share mediasoup Producer.pc 桌面分享
    this._consumers = new Map();// @type {Map<String, mediasoupClient.Consumer>}：mediasoup Consumers.
    // 存储的网络摄像头集合，可以随时进行更新
    this._webcams = new Map();// @type {Map<String, MediaDeviceInfos>}：Map of webcam MediaDeviceInfos indexed by deviceId.
    /**
    * Local Webcam. 本地当前使用的摄像头
    * @type {Object} with:
    * @param {MediaDeviceInfo} [device]
    * @param {String} [resolution] - 'qvga' / 'vga' / 'hd'.
    */
    this._webcam = {
      device: null,
      resolution: 'hd'
    };

    // Set custom SVC scalability mode.
    if (svc) {
      WEBCAM_KSVC_ENCODINGS[0].scalabilityMode = `${svc}_KEY`;
      SCREEN_SHARING_SVC_ENCODINGS[0].scalabilityMode = svc;
    }
  }

  // 关闭客户端
  close () {
    logger.info('roomClient 正在关闭...');
    if (this._closed) {
      return;
    }
    this._closed = true;
    // Close protoo Peer
    this._protoo.close();
    // Close mediasoup Transports.在上面的生产者和消费者都将关闭
    if (this._sendTransport) {
      this._sendTransport.close();
    }
    if (this._recvTransport) {
      this._recvTransport.close();
    }
    // 设置房间状态
    store.commit('setRoomState', 'closed');
    // 触发关闭事件
    this.emit('close');
  }

  // 加入会议房间
  async join () {
    logger.info('peer【peerId:%s】正在加入房间【roomId:%s】...', this._peerId, this._roomId);
    // 创建protooWebsocket 传输通道
    const webSocketTransport = new protooClient.WebSocketTransport(this._protooUrl);
    // 创建客户端peer与服务器端peer对应
    this._protoo = new protooClient.Peer(webSocketTransport);
    // 设置房间状态，加入房间中
    store.commit('setRoomState', 'connecting');
    logger.info('peer【peerId:%s】创建成功', this._peerId);
    // 进行peer的监听事件
    this._protoo.on('open', () => {
      // the transport is connected.
      logger.info('peer【peerId:%s】open连接成功!', this._peerId);
      // 当连接建立的时候，开始处理信令流程
      this._joinRoom();
    })

    this._protoo.on('failed', (currentAttempt) => {
      // Event fired when the connection with the server fails (due to network errors, not running server, unreachable server address, etc).
      //The peer will try to connect as many times as defined in its retry options. After those retries, the close event will fire..
      logger.error('peer【peerId:%s】连接失败!，当前重试连接次数【%d】', this._peerId, currentAttempt);
      // 弹框类的缓存
      store.notify({
        type: 'protooFailed',
        payload: {
          message: `peer【peerId:${this._peerId}】连接失败!，当前重试连接次数【${currentAttempt}】`
        }
      });
    })

    this._protoo.on('disconnected', () => {
      // the transport is connected.
      logger.error('peer【peerId:%s】连接突然断开!', this._peerId);
      // 会进行重连，这里先关闭之前的mediasoup连接通道
      if (this._sendTransport) {
        this._sendTransport.close();
      }
      if (this._recvTransport) {
        this._recvTransport.close();
      }
      // 设置房间状态关闭
      store.commit('setRoomState', 'closed');
    })

    this._protoo.on('close', () => {
      // Event fired when the peer is closed by calling close() on it, or when the underlying transport is remotely closed (via room.close() or peer.close() in server-side), or after all reconnection attempts fail.
      logger.info('peer【peerId:%s】连接关闭!', this._peerId);
      if (this._closed) {
        return;
      }
      // 调用关闭方法
      this.close();
    })

    this._protoo.on('request', (request, accept, reject) => {
      logger.info('peer【peerId:%s】开始处理服务器端的request【request:%o】请求', this._peerId, request);
      this._handleProtooRequest(this._protoo, request, accept, reject).catch((error) => {
        logger.error('处理proto request求失败！原因:【%o】', error);
      });
    });


    this._protoo.on('notification', (notification) => {
      logger.info('peer【peerId:%s】开始处理服务器端的notification【notification:%o】请求', this._peerId, notification);
      this._handleProtooNotification(this._protoo, notification).catch((error) => {
        logger.error('处理protoo notification失败！原因:【%o】', error);
      });
    });
  }


  // 加入房间
  async _joinRoom () {
    logger.info('peer【peerId:%s】正在加入房间room【roomId:%s】...', this._peerId, this._roomId);
    // 获取客户端设备信息
    try {
      this._mediasoupDevice = new mediasoupClient.Device();

      // 监听device 事件
      this._mediasoupDevice.observer.on('close', () => {
        logger.error('device 已经关闭！');
        this.close();
      });

      this._mediasoupDevice.observer.on("newtransport", (transport) => {
        logger.info("新的mediasoup Transport 被创建 [transportId:%s]", transport.id);
      });
      // 请求服务器端获取router的rtpCapabilities
      const routerRtpCapabilities = await this._protoo.request('getRouterRtpCapabilities');
      // 设备加载rtp能力
      await this._mediasoupDevice.load({ routerRtpCapabilities });
      // 此时设备已经准备好
      // 提前测试媒体设备
      const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
      if (!mediaStream) {
        logger.error('该设备不支持媒体流！');
        this.close();
        return;
      }
      const audioTrack = mediaStream.getAudioTracks()[0];
      // 静音
      audioTrack.enabled = fasle;
      // 定时30秒后停止
      setTimeout(() => {
        audioTrack.stop();
      }, 30000);

      // 根据传入的参数，判断该客户端是否生产数据(是否开启音视频)
      if (this._produce) {
        logger.info('正在请求服务器端创建生产者webrtcTransport传输通道...');
        // 先请求服务器端为该客户端端创建发送传输通道
        const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = await this._protoo.request('createWebRtcTransport', {
          producing: true,
          consuming: false,
          forceTcp: this._forceTcp,
          sctpCapabilities: undefined
        });
        logger.info('服务器端返回的_sendTransportId = %s', id);
        // 等待返回的数据，开始创建客户端的传输通道
        this._sendTransport = this._mediasoupDevice.createSendTransport({
          id,
          iceParameters,
          iceCandidates,
          dtlsParameters: {
            ...dtlsParameters,
            // Remote DTLS role. We know it's always 'auto' by default so, if
            // we want, we can force local WebRTC transport to be 'client' by
            // indicating 'server' here and vice-versa.
            role: 'auto'
          },
          sctpParameters,
          iceServers: [],
          proprietaryConstraints: PC_PROPRIETARY_CONSTRAINTS,
          additionalSettings: { encodedInsertableStreams: undefined }
        });
        logger.info('客户端生成的_sendTransportId = %s', id);
        // 绑定发送通道的监听事件，会在第一次produce 的时候触发该事件
        this._sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
          logger.info('发送传输通道触发连接事件，开始处理连接事件');
          // 发送连接信令
          try {
            this._protoo.request('connectWebRtcTransport', {
              transportId: this._sendTransport.id,
              dtlsParameters: dtlsParameters
            }).then(callback).catch(errback);
          } catch (error) {
            logger.error('发送传输通道【transportId:%s】连接失败！【原因:%o】', this._sendTransport.id, error);
          }
        });


        // 监听创建生产者
        this._sendTransport.on("produce", async ({ kind, rtpParameters, appData }, callback, errback) => {
          logger.info('发送传输通道正在创建生产者...');
          // Signal parameters to the server side transport and retrieve the id of 
          // the server side new producer.
          try {
            // 发送给服务端创建生产者
            const { id } = await this._protoo.request('produce', {
              transportId: this._sendTransport.id,
              kind,
              rtpParameters,
              appData
            });

            // Tell the transport that parameters were transmitted and provide it with the
            // server side producer's id.
            callback({ id });
          }
          catch (error) {
            // Tell the transport that something was wrong.
            errback(error);
          }
        });

        this._sendTransport.on('connectionstatechange', (connectionState) => {
          logger.info('发送传输通道连接状态改变了【connectionState:%o】', connectionState);
        });

        this._sendTransport.observer.on('close', () => {
          logger.error('发送传输通道【transportId:%s】关闭了', this._sendTransport.id);
        });

        this._sendTransport.observer.on('newproducer', (producer) => {
          logger.info('发送传输通道新的生产者【producerId:%s】被创建', producer.id);
        });

        this._sendTransport.observer.on('newconsumer', (consumer) => {
          logger.info('发送传输通道新的消费者【consumerId:%s】被创建', consumer.id);
        });
      }
      // 根据传入的参数，判断该客户端是否需要消费音视频数据
      if (this._consume) {
        logger.info('开始请求创建消费者传输通道...');
        const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = await this._protoo.request('createWebRtcTransport', {
          producing: false,
          consuming: true,
          forceTcp: this._forceTcp,
          sctpCapabilities: undefined
        });
        // 客户端创建接收传输通道
        this._recvTransport = this._mediasoupDevice.createRecvTransport({
          id,
          iceParameters,
          iceCandidates,
          dtlsParameters: {
            ...dtlsParameters,
            // Remote DTLS role. We know it's always 'auto' by default so, if
            // we want, we can force local WebRTC transport to be 'client' by
            // indicating 'server' here and vice-versa.
            role: 'auto'
          },
          sctpParameters,
          iceServers: [],
          additionalSettings: { encodedInsertableStreams: undefined }
        });

        // 开始监听接收传输通道
        // 绑定发送通道的监听事件
        this._recvTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
          logger.info('接收通道触发连接事件，开始处理连接事件');
          // 发送连接信令
          try {
            this._protoo.request('connectWebRtcTransport', {
              transportId: this._recvTransport.id,
              dtlsParameters: dtlsParameters
            }).then(callback).catch(errback);
          } catch (error) {
            logger.error('接收传输通道【transportId:%s】连接失败！【原因:%o】', this._sendTransport.id, error);
          }
        });


        // 创建生产者，针对接受者应该不会触发
        this._recvTransport.on("produce", async ({ kind, rtpParameters, appData }, callback, errback) => {
          logger.info('接收传输通道正在创建生产者');
        });

        this._recvTransport.on('connectionstatechange', (connectionState) => {
          logger.info('接收传输通道连接状态改变了【connectionState:%o】', connectionState);
        });

        this._recvTransport.observer.on('close', () => {
          logger.error('接收传输通道【transportId:%s】关闭了', this._sendTransport.id);
        });

        // 针对接受者不会触发
        this._recvTransport.observer.on('newproducer', (producer) => {
          logger.info('接收传输通道新的生产者【producerId:%s】被创建', producer.id);
        });

        this._recvTransport.observer.on('newconsumer', (consumer) => {
          logger.info('接收传输通道新的消费者【consumerId:%s】被创建', consumer.id);
        });
      }

      // 现在开始发送加入房间信令,如果不去消费音视频 就不要发送rtpCapabilities
      // NOTE: Don't send our RTP capabilities if we don't want to consume.
      const { peers } = await this._protoo.request('join', {
        displayName: this._displayName,
        device: this._device,
        rtpCapabilities: this._consume ? this._mediasoupDevice.rtpCapabilities : undefined,
        sctpCapabilities: undefined
      });
      // 设置房间状态,已经连接
      store.commit('setRoomState', 'connected');
      // 设置房间中其他人信息,页面渲染
      store.commit('addPeer', { peers });
      // 启动生产音视频
      if (this._produce) {
        // 启动麦克风,封装成方法后面可以外部调用
        this.enableMic();
        // 启动网络摄像头,封装成方法后面可以外部调用
        this.enableWebcam();
      }
    } catch (error) {
      logger.error('peer【peerId:%s】加入房间room【roomId:%s】失败！', this._peerId, this._roomId);
      // 弹框无法加入房间
      // 调用关闭房间
      this.close();
    }
  }

  // 处理服务端的请求
  async _handleProtooRequest (peer, request, accept, reject) {
    logger.info('正在处理protoo request 【method:%s, data:%o】请求...', request.method, request.data);
    switch (request.method) {
      case 'newConsumer': {
        // 判断是否已经加入房间
        if (this._closed) {
          logger.error('客户端房间已经关闭！');
          throw new Error('客户端房间已经关闭！');
        }
        // 判断该客户端是否想要去消费音视频数据
        if (!this._consume) {
          logger.error('客户端不想要去消费音视频数据！');
          reject(403, '客户端不想要去消费音视频数据');
        }
        // 解析服务器端发送过来的数据
        const {
          peerId,
          id,
          producerId,
          kind,
          rtpParameters,
          type,
          producerPaused,
          appData
        } = request.data;
        logger.info('服务端传递过来的生产者appData【appData:%o】', appData);
        // 处理新的消费者信息
        try {
          const consumer = await this._recvTransport.consume({
            id,
            producerId,
            kind,
            rtpParameters,
            appData: { ...appData, peerId }
          });
          // 将消费者存起来
          this._consumers.set(consumer.id, consumer);
          // 监听事件
          consumer.on('transportclose', () => {
            logger.error('客户端接收通道关闭了');
            // 从消费者中移除
            this._consumers.delete(consumer.id);
          });

          consumer.on('trackended', () => {
            logger.error('客户端接收的音视频轨道被停止了');
          });

          consumer.observer.on('close', () => {
            logger.error('消费者【consumerId:%s】关闭了', consumer.id);
          });
          consumer.observer.on('pause', () => {
            logger.error('消费者【consumerId:%s】暂停了', consumer.id);
          });
          consumer.observer.on('resume', () => {
            logger.error('消费者【consumerId:%s】恢复了', consumer.id);
          });
          // 解析出来当前消费者的同播规则
          const { spatialLayers, temporalLayers } = mediasoupClient.parseScalabilityMode(consumer.rtpParameters.encodings[0].scalabilityMode);

          // 存储起来,供前端使用
          store.commit('addConsumer', {
            id: consumer.id,
            type: type,
            locallyPaused: false,
            remotelyPaused: producerPaused,
            rtpParameters: consumer.rtpParameters,
            spatialLayers: spatialLayers,
            temporalLayers: temporalLayers,
            preferredSpatialLayer: spatialLayers - 1,
            preferredTemporalLayer: temporalLayers - 1,
            priority: 1,
            codec: consumer.rtpParameters.codecs[0].mimeType.split('/')[1],
            track: consumer.track
          });

          accept();//我们准备好了。回答protoo请求，以便服务器将恢复此消费者（如果是视频则暂时暂停）。
          if (consumer.kind === 'video' && store.commit('getState').me.audioOnly) {
            this._pauseConsumer(consumer);// 如果启用了纯音频模式，请将其暂停。
          }
        } catch (error) {
          logger.error('客户端创建生视频生产者失败！');
          throw new Error('客户端创建生视频生产者失败！');
        }
        break;
      }
      default: {
        logger.error('没有匹配到合适的方法来处理protoo request【method:%s】', request.method);
        break;
      }

    }
  }
  // 处理服务端的通知
  _handleProtooNotification (peer, notification) {
    logger.info('正在处理服务器端的通知，方法【method:%s】通知数据【data:%o】', request.method, request.data);
    switch (notification.method) {
      // 新的peer 产生
      case 'newPeer': {
        // 获取通知的数据
        const {
          id,
          device,
          displayName
        } = notification.data;
        // 将peer存储起来
        store.commit('addPeer', {
          id,
          device,
          displayName
        });
        break;
      }

      // peer 关闭的通知
      case 'peerClosed': {
        // 获取数据
        const { peerId } = notification.data;
        // 从缓存中删除对应的peer
        store.commit('removePeer', {
          peerId
        });
        break;
      }
      // 其他客户端名称的改变
      case 'peerDisplayNameChanged': {
        // 获取数据
        const {
          peerId,
          displayName,
          oldDisplayName
        } = notification.data;
        // 更新设置客户端名称的改变
        store.commit('setPeerDisplayName', {
          peerId,
          displayName
        });
        break;
      }
      // 生产者的得分
      case 'producerScore': {
        // 接收数据
        const {
          producerId,
          score
        } = notification.data;
        // 设置生产者的得分情况
        store.commit('setProducerScore', {
          producerId,
          score
        });
        break;
      }
      // 消费者得分
      case 'consumerScore': {
        const {
          consumerId,
          score
        } = notification.data;
        store.commit('setConsumerScore', {
          consumerId,
          score
        });
        break;
      }
      //本客户端对应的某个生产者（被消费者）暂停
      case 'consumerPaused': {
        const { consumerId } = notification.data;
        const consumer = this._consumers.get(consumerId);
        if (!consumer) {
          logger.error('根据消费者id【consumerId:%s】没有找到该消费者', consumerId);
          break;
        }
        consumer.pause();
        store.commit('setConsumerPaused', consumerId, 'remote');
        break;
      }
      // 消费者恢复
      case 'consumerResumed': {
        const { consumerId } = notification.data;
        const consumer = this._consumers.get(consumerId);
        if (!consumer) {
          logger.error('根据消费者id【consumerId:%s】没有找到该消费者', consumerId);
          break;
        }
        consumer.resume();
        store.commit('setConsumerPaused', consumerId, 'remote');
        break;
      }
      // 消费者关闭、
      case 'consumerClosed': {
        // 获取数据
        const { consumerId } = notification.data;
        const consumer = this._consumers.get(consumerId);
        if (!consumer) {
          logger.error('根据消费者id【consumerId:%s】没有找到该消费者', consumerId);
          break;
        }
        // 从本缓存中删除
        this._consumers.delete(consumerId);
        // 从消费者中获取peerId
        const { peerId } = consumer.appData;
        // 从应用缓存中移除
        store.commit('removeConsumer', {
          consumerId,
          peerId
        });
        break;
      }
      // 设置消费者层的改变
      case 'consumerLayersChanged': {
        const {
          consumerId,
          spatialLayer,
          temporalLayer
        } = notification.data;
        const consumer = this._consumers.get(consumerId);
        if (!consumer) {
          logger.error('根据消费者id【consumerId:%s】没有找到该消费者', consumerId);
          break;
        }
        store.commit('setConsumerCurrentLayers', {
          consumerId,
          spatialLayer,
          temporalLayer
        });
      }
      // 声音检测
      case 'activeSpeaker': {
        const { peerId } = notification.data;
        store.commit('setRoomActiveSpeaker', { peerId });
        break;
      }
      default: {
        logger.error('没有匹配到通知类的方法！');
        break;
      }


    }
  }
  // 暂停消费者
  async _pauseConsumer (consumer) {
    logger.info('正在暂停消费者【consumerId:%s】', consumer.id);
    if (!consumer || consumer.paused) {
      return;
    }

    try {
      // 请求服务器端暂停消费者
      await this._protoo.request('pauseConsumer', {
        consumerId: consumer.id
      });
      // 服务器暂停后，再暂停客户端的消费者
      consumer.pause();
      // 存储状态
      store.commit('setConsumerPaused', consumer.id, 'local');
    } catch (error) {
      logger.error('暂停客户端消费者失败');
    }
  }
  // 恢复消费者
  async _resumeConsumer (consumer) {
    logger.info('正在恢复消费者...');
    // 判断消费者是否存在
    if (!consumer || !consumer.paused) {
      logger.error('恢复消费者时不存在或消费者未暂停消费！');
      return;
    }
    try {
      await this._protoo.request('resumeConsumer', {
        consumerId: consumer.id
      });
      // 等待服务器端消费者恢复后，客户端在进行恢复消费
      consumer.resume();
      // 存储消费者的恢复
      store.commit('setConsumerResumed', consumer.id, 'local');
    } catch (error) {
      logger.error('恢复消费者失败！');
    }
  }
  // 更新网络摄像头设备信息
  async _updateWebcams () {
    logger.info('开始更新网络摄像头信息...');
    // 重置_webCams
    this._webcams = new Map();
    //获取本地媒体设备的信息
    const devices = await navigator.mediaDevices.enumerateDevices();
    // 循环该网络摄像头设备
    for (const device of devices) {
      if (device.kind !== 'videoinput') {
        continue;
      }
      // 设置到属性_webcams 中
      this._webcams.set(device.deviceId, device);
    }
    // 获取当前所有的网路设备数组
    const webcamArray = Array.from(this._webcams.values());
    logger.info('当前的所有网络摄像头设备列表【_webcams:%o】', webcamArray);
    logger.info('获取当前使用的网络摄像投头设备信息');
    // 获取当前使用的网络摄像头设备id
    let currentWebcamDeviceId = this._webcam.device ? this._webcam.device.deviceId : undefined;
    // 给当前正在使用的网络摄像头设备赋值
    if (webcamArray.length == 0) {
      this._webcam.device = null;
    } else if (!this._webcams.has(currentWebcamDeviceId)) {
      // 使用新加入的设备为当前网络摄像头
      this._webcam.device = webcamArray[0];
    }
    // 设置可以改变摄像头
    store.commit('setCanChangeWebcam', this._webcams.size > 1);

  }
  // 获取摄像头类型
  _getWebcamType (device) {
    logger.info('正在获取摄像头类型...');
    if (/(back|rear)/i.test(device.label)) {
      logger.info('_getWebcamType() | 后置摄像头');
      return 'back';
    } else {
      logger.info('_getWebcamType() | 前置摄像头');
      return 'front';
    }
  }
  // 启用麦克风
  async enableMic () {
    logger.info('正在开启麦克风...');
    // 判断micProducer是否已经存在
    if (this._micProducer) {
      logger.info('麦克风生产者已经存在');
      return;
    }
    // 判断设备是否支持生产者
    let kind = 'audio';
    if (!device.canProduce(kind)) {
      logger.error('设备不能生产该类型【kind:%s】的编码', kind);
      return;
    }
    // 定义媒体轨道
    let audioTrack;
    try {
      // 获取设备媒体流
      const localAudioMediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
      audioTrack = localAudioMediaStream.getAudioTracks()[0];
      // 创建生产者
      this._micProducer = await this._sendTransport.produce({
        track: audioTrack,
        codecOptions: {
          opusStereo: 1,
          opusDtx: 1
        }
      });
      logger.info('成功创建本地生产者【micProducerId:%s】', this._micProducer.id);

      // 存储生产者
      store.commit('addProducer', {
        id: this._micProducer.id,
        paused: this._micProducer.paused,
        track: this._micProducer.track,
        rtpParameters: this._micProducer.rtpParameters,
        codec: this._micProducer.rtpParameters.codecs[0].mimeType.split('/')[1]
      });


      // 监听事件
      this._micProducer.on('transportclose', () => {
        logger.error("麦克风生产者【micProducerId:%s】监听到发送传输通道【sendTransportId:%s】关闭了", this._micProducer.id, this._sendTransport.id);
        // 麦克风生产者设置为null
        this._micProducer = null;
      });

      this._micProducer.on("trackended", () => {
        logger.warn("麦克风媒体轨道【track:%o】结束了", track);
        // 关闭麦克风
        this.disableMic().catch((error) => {
          logger.error('关闭麦克风异常！%o', error);
        });
      });

      this._micProducer.observer.on('close', () => {
        logger.error('麦克风生产者【micProducerId:%s】关闭', this._micProducer.id);
      });

      this._micProducer.observer.on('pause', () => {
        logger.error('麦克风生产者【micProducerId:%s】暂停', this._micProducer.id);
      });
      this._micProducer.observer.on('resume', () => {
        logger.error('麦克风生产者【micProducerId:%s】恢复', this._micProducer.id);
      });
      this._micProducer.observer.on('trackended', () => {
        logger.error('麦克风生产者【micProducerId:%s】轨道【track:%o】结束', this._micProducer.id, track);
      });
    } catch (error) {
      logger.error('创建麦克风生产者失败！');
      // 判断track
      if (track) {
        track.stop();
      }
    }

  }
  // 关闭麦克风
  async disableMic () {
    logger.info('开始关闭麦克风');
    // 判断是否已经处理过
    if (!this._micProducer) {
      return;
    }
    this._micProducer.close();
    // 移除生产者
    store.commit('removeProducer', this._micProducer.id);
    try {
      await this._protoo.request('closeProducer', {
        producerId: this._micProducer.id
      });
    } catch (error) {
      logger.error('发送给服务器端移除麦克风生产者请求失败');
    }
    this._micProducer = null;
  }
  // 暂停（静音）麦克风
  async muteMic () {
    logger.info('正在静音麦克风...');
    if (!this._micProducer) {
      return;
    }
    // 暂停麦克风
    this._micProducer.pause();
    try {
      // 发送给服务器端让服务器端次生产者静音
      await this._protoo.request('pauseProducer', {
        producerId: this._micProducer.id
      });
      // 存储暂停状态
      store.commit('setProducerPaused', this._micProducer.id);
    } catch (error) {
      logger.info('麦克风静音失败！');
    }
  }
  // 恢复麦克风
  async unmuteMic () {
    logger.info('正在恢复麦克风...');
    // 判断当前麦克风是否有效
    if (!this._micProducer) {
      return;
    }
    this._micProducer.resume();
    try {
      await this._protoo.request('resumeProducer', {
        producerId: this._micProducer.id
      });
      store.commit('setProducerResumed', this._micProducer.id);
    } catch (error) {
      logger.error('恢复麦克风失败！');
    }
  }

  // 开启扬声器
  async enableLoudspeaker () {

  }

  // 关闭扬声器
  async disableLoudspeaker () {

  }
  // 启用网络摄像头
  async enableWebcam () {
    logger.info('正在启动网络摄像头生产者...');
    // 判断是否已经有网络摄像头生产者
    if (this._webcamProducer) {
      logger.info('已经存在网络摄像头生产者');
      return;
    } else if (this._shareProducer) {
      logger.info('在开启网络摄像头的时候存在桌面分享者，现在进行关闭桌面分享者');
      // 判断是否存在桌面分享生产者
      // 关闭桌面分享生产者
      await this.disableShare();
    }
    // 判断设备是否支持video
    let kind = 'video';
    if (!this._mediasoupDevice.canProduce(kind)) {
      logger.error('客户端设备不支持kind【kind:%s】类型的编码', kind);
      return;
    }

    // 定义track 和 客户端设备
    let localVideoTrack;
    let device;
    try {
      // 更新_webcams属性网络设备
      await this._updateWebcams();

      //device 类型MediaDeviceInfo  MDN查看
      device = this._webcam.device;
      // 获取当前使用的的设备信息
      if (!device) {
        logger.error('获取当前使用的网络摄像头失败！');
        throw new Error('获取当前使用的网络摄像头失败');
      }
      // video 分辨率,字符串标识
      const resolution = this._webcam.resolution;
      // 开始调用本地媒体摄像头
      const localVideoMediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          deviceId: { ideal: device.deviceId },
          ...VIDEO_CONSTRAINS[resolution]
        }
      });
      // 获取视频轨道
      localVideoTrack = localVideoMediaStream.getVideoTracks()[0];

      // 判断参数以及设置视频编码
      let encodings;
      let codec;
      // 定义video 开始帧率
      const codecOptions = {
        videoGoogleStartBitrate : 1000
      }
      // 判断是否强制使用h264 
      if (this._forceH264) {
        // 获取编解码器能力
        codec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.mimeType.toLowerCase() === 'video/h264' });
        if (!codec) {
          throw new Error('不支持所需的 H264 编解码器配置!');
        }
      } else if (this._forceVP9) {
        codec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.mimeType.toLowerCase() === 'video/vp9' });
        if (!codec) {
          throw new Error('不支持所需的 VP9 编解码器配置!');
        }
      }

      // 判断是否使用功能同播
      if (this._useSimulcast) {
        const firstVideoCodec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.kind = kind });
        // 如果 VP9 是唯一可用的视频编解码器，则使用 SVC
        if ((this._forceVP9 && codec) || firstVideoCodec.mimeType.toLowerCase() === 'video/vp9') {
          encodings = WEBCAM_KSVC_ENCODINGS;
        } else {
          encodings = WEBCAM_SIMULCAST_ENCODINGS;
        }
      }

      // 网络摄像头生产者生产数据
      this._webcamProducer = await transport.produce({
        track: localVideoTrack,
        encodings,
        codecOptions,
        codec
      });

      // 存储视频生产者信息数据
      store.commit('addProducer', {
        id: this._webcamProducer.id,
        deviceLabel: device.label,
        type: this._getWebcamType(device),
        paused: this._webcamProducer.paused,
        track: this._webcamProducer.track,
        rtpParameters: this._webcamProducer.rtpParameters,
        codec: this._webcamProducer.rtpParameters.codecs[0].mimeType.split('/')[1]
      });
      // 绑定监听事件
      // 监听事件
      this._webcamProducer.on('transportclose', () => {
        logger.error("视频生产者【webcamProducerId:%s】监听到发送传输通道【sendTransportId:%s】关闭了", this._webcamProducer.id, this._sendTransport.id);
        // 麦克风生产者设置为null
        this._webcamProducer = null;
      });

      this._webcamProducer.on("trackended", () => {
        logger.warn("视频媒体轨道【track:%o】结束了", localVideoTrack);
        // 关闭麦克风
        this.disableWebcam().catch((error) => {
          logger.error('关闭视频异常！%o', error);
        });
      });

      this._webcamProducer.observer.on('close', () => {
        logger.error('视频生产者【webcamProducerId:%s】关闭', this._webcamProducer.id);
      });

      this._webcamProducer.observer.on('pause', () => {
        logger.error('视频生产者【webcamProducerId:%s】暂停', this._webcamProducer.id);
      });
      this._webcamProducer.observer.on('resume', () => {
        logger.error('视频生产者【webcamProducerId:%s】恢复', this._webcamProducer.id);
      });
      this._webcamProducer.observer.on('trackended', () => {
        logger.error('视频生产者【webcamProducerId:%s】轨道【track:%o】结束', this._webcamProducer.id, localVideoTrack);
      });
    } catch (error) {
      logger.error('开启视频生产者失败！');
      // 判断本地的视频轨道是否存在如果存在则关闭停止
      if (localVideoTrack) {
        localVideoTrack.stop();
      }
    }


  }

  // 关闭网络摄像头
  async disableWebcam () {
    logger.info('正在关闭网络摄像头...');
    if (!this._webcamProducer) {
      return;
    }
    this._webcamProducer.close();
    // 客户端移除生产者
    store.commit('removeProducer', this._webcamProducer.id);
    // 请求服务器端关闭摄像头生产者并移除
    try {
      this._protoo.request('closeProducer', {
        producerId: this._webcamProducer.id
      });
    } catch (error) {
      logger.error('客户端请求服务器端关闭摄像头生产者失败！');
    }

    this._webcamProducer = null;


  }
  // 改变网络摄像头的视频质量，循环切换三种不同的视频质量
  async changeWebcamResolution () {
    logger.info('正在改变摄像头的视频质量...');
    try {
      // 判断当前使用的摄像头的视频质量
      switch (this._webcam.resolution) {
        case 'hd': {
          this._webcam.resolution = VIDEO_CONSTRAINS['qvga']
          break;
        }
        case 'qvga': {
          this._webcam.resolution = VIDEO_CONSTRAINS['vga']
          break;
        }
        case 'vga': {
          this._webcam.resolution = VIDEO_CONSTRAINS['hd']
          break;
        }
        default: {
          this._webcam.resolution = VIDEO_CONSTRAINS['hd']
          break;
        }
      }
      // 重新替换生产者媒体轨道
      const newLocalVideoMediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          deviceId: {
            exact: this._webcam.device.deviceId
          },
          ...VIDEO_CONSTRAINS[this._webcam.resolution]
        }
      });
      // 获取新的video媒体轨道
      const newLocalVideoMediaTrack = newLocalVideoMediaStream.getVideoTracks()[0];
      // 替换视频生产者的媒体轨道
      this._webcamProducer.replaceTrack({ track: newLocalVideoMediaTrack });
    } catch (error) {
      logger.error('改变摄像头的视频质量失败！');
    }
  }
  // 切换（改变）摄像头
  async changeWebcam () {
    logger.info('正在切换摄像头...');

    // 接着获取当前使用的摄像头设备信息
    try {
      // 先更新一下所有的摄像头
      this._updateWebcams();
      // 获取所有可用的摄像头设备
      const webcamDeviceIds = Array.from(this._webcams.keys());
      // 获取当前使用的摄像头
      const currentWebcamDeviceId = this._webcam.device ? this._webcam.device.deviceId : undefined;
      // 判断当前使用的摄像头是否在设别列表中,并找出索引
      const existIndex = webcamDeviceIds.indexOf(currentWebcamDeviceId);
      //  下面的逻辑会形成一个闭环
      if (existIndex < webcamDeviceIds.length - 1) {
        existIndex++;
      } else {
        existIndex = 0;
      }
      // 获取选择的设备，并赋值给当前使用的设备
      this._webcam.device = this._webcams.get(webcamDeviceIds[existIndex]);

      // 判断当前设备是否存在
      if (!this._webcam.device) {
        logger.error('切换摄像头时检测当前不存在任何摄像头设备！');
        throw new Error('切换摄像头时检测当前不存在任何摄像头设备');
      }
      // 重置该摄像质量为hd
      // Reset video resolution to HD.
      this._webcam.resolution = 'hd';
      // 由于在移动端可能不能前后摄像头同时存在，所以先暂停当前使用的生产者轨道
      this._webcamProducer.track.stop();
      // 获取指定的摄像头所产生的媒体轨道
      const newVideoMediaStream = await navigator.mediaDevices.getUserMedia({
        video: {
          deviceId: {
            exact: this._webcam.device.deviceId
          },
          ...VIDEO_CONSTRAINS[this._webcam.resolution]
        }
      });
      // 获取对应的轨道
      const newVideoTrack = newVideoMediaStream.getVideoTracks()[0];
      // 将生产者的轨道进行替换
      this._webcamProducer.replaceTrack({ track: newVideoTrack });
      // 存储新的媒体轨道
      store.commit('setProducerTrack', this._webcamProducer.id, newVideoTrack);
    } catch (error) {
      logger.error('切换摄像头失败！');
    }
  }

  // 开启桌面共享
  async enableShare () {
    logger.info('正在开启分享桌面...');
    // 判断分享桌面生产者是否已经存在
    if (this._shareProducer) {
      return;
    } else if (this._webcamProducer) {
      // 判断是否有正在生产的video生产者
      logger.info('关闭正在使用的网络摄像头...');
      await this.disableWebcam();
    }
    // 判断当前设备是否支持video 生产者
    if (!this._mediasoupDevice.canProduce('video')) {
      logger.error('当前设备不支持video生产者！');
    }
    // 定义桌面媒体轨道
    let shareVideoMediaTrack;
    // 获取媒体设备的桌面流
    try {
      const shareVideoMediaStream = await navigator.mediaDevices.getDisplayMedia({
        audio: fasle,
        video: {
          displaySurface: 'monitor',
          logicalSurface: true,
          cursor: true,
          width: { max: 1920 },
          height: { max: 1080 },
          frameRate: { max: 30 }
        }
      });
      // 由于在某些实现中流可能已经关闭
      if (!shareVideoMediaStream) {
        logger.error('当前分享的媒体流已经关闭了！');
        return;
      }
      // 如果没有关闭,则获取媒体轨道
      shareVideoMediaTrack = shareVideoMediaStream.getVideoTracks()[0];
      // 根据前面传入的参数获取编码以及codec的相关配置
      let encodings;
      let codec;
      const codecOptions = { videoGoogleStartBitrate: 1000 }
      // 判断是否强制使用h264 
      if (this._forceH264) {
        // 获取编解码器能力
        codec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.mimeType.toLowerCase() === 'video/h264' });
        if (!codec) {
          throw new Error('不支持所需的 H264 编解码器配置!');
        }
      } else if (this._forceVP9) {
        codec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.mimeType.toLowerCase() === 'video/vp9' });
        if (!codec) {
          throw new Error('不支持所需的 VP9 编解码器配置!');
        }
      }

      // 判断是否使用功能同播
      if (this._useSharingSimulcast) {
        const firstVideoCodec = this._mediasoupDevice.rtpCapabilities.codecs.find((codec) => { codec.kind = 'video' });
        // 如果 VP9 是唯一可用的视频编解码器，则使用 SVC
        if ((this._forceVP9 && codec) || firstVideoCodec.mimeType.toLowerCase() === 'video/vp9') {
          encodings = SCREEN_SHARING_SVC_ENCODINGS;
        } else {
          encodings = SCREEN_SHARING_SIMULCAST_ENCODINGS.map((encoding) => {
            return { ...encoding, dtx: true }
          });
        }
      }
      // 开始构建生产者
      this._shareProducer = await this._sendTransport.produce({
        track: shareVideoMediaTrack,
        encodings,
        codecOptions,
        codec,
        appData: {
          share: true
        }
      });
      // 存储生产者
      store.commit('addProducer', {
        id: this._shareProducer.id,
        type: 'share',
        paused: this._shareProducer.paused,
        track: this._shareProducer.track,
        rtpParameters: this._shareProducer.rtpParameters,
        codec: this._shareProducer.rtpParameters.codecs[0].mimeType.split('/')[1]
      });
      // 监听分享桌面生产者事件
      // 监听事件
      this._shareProducer.on('transportclose', () => {
        logger.error("桌面生产者【shareProducerId:%s】监听到发送传输通道【sendTransportId:%s】关闭了", this._shareProducer.id, this._sendTransport.id);
        // 麦克风生产者设置为null
        this._shareProducer = null;
      });

      this._shareProducer.on("trackended", () => {
        logger.warn("桌面媒体轨道【track:%o】结束了", shareVideoMediaTrack);
        // 关闭麦克风
        this.disableShare().catch((error) => {
          logger.error('关闭桌面异常！%o', error);
        });
      });

      this._shareProducer.observer.on('close', () => {
        logger.error('桌面生产者【shareProducerId:%s】关闭', this._shareProducer.id);
      });

      this._shareProducer.observer.on('pause', () => {
        logger.error('桌面生产者【shareProducerId:%s】暂停', this._shareProducer.id);
      });
      this._shareProducer.observer.on('resume', () => {
        logger.error('桌面生产者【shareProducerId:%s】恢复', this._shareProducer.id);
      });
      this._shareProducer.observer.on('trackended', () => {
        logger.error('桌面生产者【shareProducerId:%s】轨道【track:%o】结束', this._shareProducer.id, shareVideoMediaTrack);
      });
    } catch (error) {
      logger.error('获取桌面流失败！');
    }
  }
  // 禁止桌面共享
  async disableShare () {
    logger.info('正在关闭桌面分享者...');
    if (!this._shareProducer) {
      return;
    }
    this._shareProducer.close();
    // 存储状态
    store.commit('removeProducer', this._shareProducer.id);
    // 通知服务端关闭桌面共享者
    try {
      await this._protoo.request('closeProducer', {
        producerId: this._shareProducer.id
      });
    } catch (error) {
      logger.error('发送给服务端的请求关闭桌面分享失败！');
    }
    // 重置为null
    this._shareProducer = null;
  }
  // 设置最大发送端时间层
  async setMaxSendingSpatialLayer (spatialLayer) {
    logger.info('正在设置最大生产者的时间层...');
    try {
      // 判断是摄像头还是分享桌面
      if (this._webcamProducer) {
        this._webcamProducer.setMaxSpatialLayer(spatialLayer);
      } else if (this._shareProducer) {
        this._shareProducer.setMaxSpatialLayer(spatialLayer)
      }
    } catch (error) {
      logger.error('设置生产者最大时间层失败！');
    }
  }
  // 设置消费者更信赖的层数
  async setConsumerPreferredLayers (consumerId, spatialLayer, temporalLayer) {
    logger.info('正在设置消费者更信赖的层数...');
    try {
      await this._protoo.request('setConsumerPreferredLayers', {
        consumerId,
        spatialLayer,
        temporalLayer
      });
      // 存储消费者的层数
      store.commit('setConsumerPreferredLayers', consumerId, spatialLayer, temporalLayer);
    } catch (error) {
      logger.error('设置服务器端消费者更信赖的层数失败！');
    }
  }
  // 设置消费者优先级
  async setConsumerPriority (consumerId, priority) {
    logger.info('正在设置消费者优先级...');
    try {
      await this._protoo.request('setConsumerPriority', {
        consumerId,
        priority
      });
      // 存储
      store.commit('setConsumerPriority', consumerId, priority);
    } catch (error) {
      logger.error('设置服务器端消费者优先级失败！');
    }
  }
  // 请求消费者关键帧
  async requestConsumerKeyFrame (consumerId) {
    logger.info('正在请求消费者关键帧...');
    try {
      this._protoo.request('requestConsumerKeyFrame', {
        consumerId: consumerId
      });
      // 存储通知信息
    } catch (error) {
      logger.error('请求服务器端消费者关键帧失败！');
    }
  }
  // 重启ice
  async restartIce () {
    logger.info('正在重启ice...');
    try {
      // 判断发送传输通道和接收传输通道是否存在
      if (this._sendTransport) {
        const { iceParameters } = await this._protoo.request('restartIce', {
          transportId: this._sendTransport.id
        });
        // 接收到服务器端回复后重启客户端ice
        this._sendTransport.restartIce({ iceParameters })
      }
      // 重启接收端ice
      if (this._recvTransport) {
        const { iceParameters } = await this._protoo.request('restartIce', {
          transportId: this._recvTransport.id
        });
        // 重启客户端ice
        this._recvTransport.restartIce({ iceParameters });
      }
    } catch (error) {
      logger.error('重启ice失败！');
    }
  }
  // 改变展示名称
  async changeDisplayName (displayName) {
    logger.info('正在改变展示名称');
    const oldDisplayName = this._displayName;
    try {
      await this._protoo.request('changeDisplayName', {
        displayName
      });
      this._displayName = displayName;
      // 存储起来
      store.commit('setDisplayName', displayName);
    } catch (error) {
      logger.error('客户端请求服务器端改变展示名称失败！');
      // 如果失败重新讲值赋值
      this._displayName = oldDisplayName;
      store.commit('setDisplayName', oldDisplayName);
    }
  }
  // 获取远端发送传输通道的统计信息
  async getSendTransportRemoteStats () {
    logger.info('正在获取发送传输通道的统计信息...');
    if (!this._sendTransport) {
      return;
    }
    let stats;
    try {
      stats = await this._protoo.request('getTransportStats', {
        transportId: this._sendTransport.id
      });
    } catch (error) {
      logger.error('获取远端发送传输通道的统计信息失败！');
    }
    return stats
  }
  // 获取本地发送传输通道的统计信息
  async getSendTransportLocalStats () {
    logger.info('正在获取本地发送传输通道的统计信息...');
    if (this._sendTransport) {
      return this._sendTransport.getStats();
    }
  }
  // 获取远端接收传输通道的统计信息
  async getRecvTransportRemoteStats () {
    logger.info('正在获取远端接收传输通道的统计信息...');
    if (!this._recvTransport) {
      return;
    }
    let stats;
    try {
      stats = await this._protoo.request('getTransportStats', {
        transportId: this._recvTransport.id
      });
    } catch (error) {
      logger.error('获取远端接收传输通道的统计信息失败！');
    }
    return stats;
  }
  // 获取本地接收传输通道的统计信息
  async getRecvTransportLocalStats () {
    logger.info('正在获取本地接收传输通道的统计信息...');
    if (this._recvTransport) {
      this._recvTransport.getStats();
    }
  }
  // 获取远端音频的统计信息
  async getAudioRemoteStats () {
    logger.info('正在获取远端音频的统计信息...');
    if (!this._micProducer) {
      return;
    }
    let stats;
    try {
      stats = await this._protoo.request('getProducerStats', {
        producerId: this._micProducer.id
      });
    } catch (error) {
      logger.error('获取远端音视频的统计信息失败！');
    }
    return stats;
  }
  // 获取本地音频的统计信息
  async getAudioLocalStats () {
    logger.info('正在获取本地的音频统计信息...');
    if (this._micProducer) {
      this._micProducer.getStats();
    }
  }
  // 获取远端视频的统计信息
  async getVideoRemoteStats () {
    logger.info('正在获取远端视频的统计信息...');
    const producer = this._webcamProducer || this._shareProducer;
    if (!producer) {
      return;
    }
    let stats;
    try {
      stats = await this._protoo.request('getProducerStats', {
        producerId: producer.id
      });
    } catch (error) {
      logger.error('获取远端视频的统计信息失败！');
    }
    return stats;
  }
  // 获取本地视频的统计信息
  async getVideoLocalStats () {
    logger.info('正在获取本地的视频统计信息...');
    const producer = this._webcamProducer || this._shareProducer;
    if (producer) {
      return producer.getStats();
    }
  }
  // 获取远端消费者的统计信息
  async getConsumerRemoteStats (consumerId) {
    logger.info('正在获取远端消费者的统计信息...');
    let stats;
    try {
      stats = await this._protoo.request('getConsumerStats', {
        consumerId: consumerId
      });
    } catch (error) {
      logger.error('获取远端消费者的统计信息失败！');
    }
    return stats;
  }
  // 获取本地消费者的统计信息
  async getConsumerLocalStats (consumerId) {
    logger.info('正在获取本地消费者的统计信息...');
    const consumer = this._consumers.get(consumerId);
    if (consumer) {
      consumer.getStats();
    }
  }
}

module.exports = RoomClient;